/*
 * @(#)NetTran.java 2008-12-11
 * 
 */

package com.air.ota.epay.util;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Enumeration;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;



public class NetTrans {
	/**
	 * 密钥库密码
	 */
	private String keyStorePass;
	
	/**
	 * 字符集
	 */
	private String chartset;
	
	private final static String JKS = "JKS";
	
	private final static String  PKCS12 = "PKCS12";
	
	private String cAType=JKS; //默认jks  PKCS12

	/**
	 * @param cAType 证书类型
	 * @param chartset 字符集编码
	 */
	public NetTrans(String cAType,String chartset) {
		this.cAType = cAType;
		this.chartset = chartset;
		Security.addProvider(new BouncyCastleProvider());
	}
	
	public NetTrans(String chartset){
		this.chartset = chartset;
		Security.addProvider(new BouncyCastleProvider());
	}
	
	public String getChartset() {
		return chartset;
	}

	public void setChartset(String chartset) {
		this.chartset = chartset;
	}

	/**
	 * 用于pkcs12 证书
	 * @param tobeEncrypted 要加密的数据
	 * @param certFile 公钥证书文件名
	 * @return 加密成功返回true，否则返回false
	 */
	public String encryptMsg(String tobeEncrypted, String certFile) throws Exception{
		return this.encryptMsg(tobeEncrypted, certFile, "");
	}

	/**
	 * 用于jks格式的密钥仓库
	 * @param tobeEncrypted 要加密的数据
	 * @param certFile 密钥库
	 * @param alias 证书别名
	 * @return 加密成功返回true,否则返回false
	 */
	public String encryptMsg(String tobeEncrypted, String certFile,String alias)throws Exception {
		InputStream certfile = null;
		try {
			certfile = this.getClass().getClassLoader().getResourceAsStream(certFile);
			RSAPublicKey pubkey=null;
			if (this.cAType.equals(PKCS12)) {
				CertificateFactory cf = CertificateFactory.getInstance("X.509");
				X509Certificate x509cert = (X509Certificate) cf
						.generateCertificate(certfile);
				pubkey = (RSAPublicKey) x509cert.getPublicKey();
			} else /*if (this.cAType.equalsIgnoreCase(JKS))*/{
				KeyStore ks=KeyStore.getInstance("JKS");
				if (this.keyStorePass==null|| this.keyStorePass.equals("")){
					throw new Exception("Error Number:-10008, Error Description: keystore password can't null");
					
				}
				ks.load(certfile,this.keyStorePass.toCharArray());
				if (alias==null||alias.equals("")){
					throw new Exception( "Error Number:-10007, Error Description: alias can't null");
					
				}
				java.security.cert.Certificate c=ks.getCertificate(alias);//alias为条目的别名
				pubkey = (RSAPublicKey)c.getPublicKey();
				
			}
			
			BigInteger mod = pubkey.getModulus();
			int keylen = mod.bitLength() / 8;
			if (tobeEncrypted.length() > keylen - 11) {
				Cipher pub = Cipher.getInstance("RSA/NONE/PKCS1Padding", "BC");
				pub.init(3, pubkey);
				KeyGenerator kp = KeyGenerator.getInstance("DESEDE");
				kp.init(new SecureRandom());
				javax.crypto.SecretKey sk = kp.generateKey();
				byte wrappedKey[] = pub.wrap(sk);
				pub = Cipher.getInstance("DESEDE/OFB/NoPadding");
				pub.init(1, sk);
				byte encrypted[] = pub.doFinal(tobeEncrypted.getBytes(this.chartset));
				byte iv[] = pub.getIV();
				byte enc_ascii[] = new byte[encrypted.length * 2];
				byte iv_asc[] = new byte[iv.length * 2];
				byte prikey_asc[] = new byte[wrappedKey.length * 2];
				hex2Ascii(encrypted.length, encrypted, enc_ascii);
				hex2Ascii(iv.length, iv, iv_asc);
				hex2Ascii(wrappedKey.length, wrappedKey, prikey_asc);
				return new String(iv_asc,this.chartset) + new String(prikey_asc,this.chartset)
						+ new String(enc_ascii,this.chartset);

				
			} else {
				Cipher pub = Cipher.getInstance("RSA/NONE/PKCS1Padding", "BC");
				pub.init(1, pubkey);
				byte encrypted[] = pub.doFinal(tobeEncrypted.getBytes(this.chartset));
				byte enc_ascii[] = new byte[encrypted.length * 2];
				hex2Ascii(encrypted.length, encrypted, enc_ascii);
				return new String(enc_ascii,this.chartset);
				
			}
		} catch (GeneralSecurityException e) {
			throw new Exception( "Error Number:-10022, Error Description: ER_ENCRYPT_ERROR"
					+ e.getMessage());
			//return false;
		} catch (IOException e){
			throw new Exception( "Error Number:-10006, Error Description: ER_IO_ERROR"
				+ e.getMessage());
		
		} finally {
			try {
				if (certfile != null)
					certfile.close();
			} catch (IOException e) {
			}
		}
	}
	
	


	/**
	 * 用pkcs12证书解密
	 * @param tobeDecrypted
	 * @param keyFile
	 * @param password
	 * @return
	 */
	public String decryptMsg(String tobeDecrypted, String keyFile,
			String password) throws Exception{
		return this.decryptMsg(tobeDecrypted, keyFile, password,"");
	}
	
	
	/**
	 * 用jks证书解密
	 * @param tobeDecrypted
	 * @param keyFile
	 * @param password
	 * @param alias
	 * @return
	 */
	public String decryptMsg(String tobeDecrypted, String keyFile,
			String password, String alias) throws Exception {
		InputStream in = null;
		try {
			KeyStore ks ;
			RSAPrivateCrtKey prikey;
			in = this.getClass().getClassLoader().getResourceAsStream(keyFile);
			
			if (this.cAType.equals(PKCS12)){
				ks = KeyStore.getInstance("PKCS12");
				ks.load(in, password.toCharArray());
				Enumeration<String> myEnum = ks.aliases();
				String keyAlias = null;
				keyAlias = (String) myEnum.nextElement();
				prikey = (RSAPrivateCrtKey) ks.getKey(keyAlias,
						password.toCharArray());
			} else{
				ks=KeyStore.getInstance("JKS");
				
				if (this.keyStorePass==null|| this.keyStorePass.equals("")){
					throw new Exception( "Error Number:-10008, Error Description: keystore password can't null");
					//return false;
				}
				
				ks.load(in,this.keyStorePass.toCharArray());
				prikey=(RSAPrivateCrtKey)ks.getKey(alias,password.toCharArray());
			}						
			
			BigInteger mod = prikey.getModulus();
			int keylen = mod.bitLength() / 8;
			if (tobeDecrypted.length() > keylen * 2) {
				byte iv_asc[] = tobeDecrypted.substring(0, 16).getBytes(this.chartset);
				byte prikey_asc[] = tobeDecrypted.substring(iv_asc.length,
						iv_asc.length + keylen * 2).getBytes(this.chartset);
				byte enc_ascii[] = tobeDecrypted.substring(
						iv_asc.length + keylen * 2).getBytes(this.chartset);
				byte iv[] = new byte[8];
				byte unwrappedkey[] = new byte[prikey_asc.length / 2];
				byte decrypted[] = new byte[enc_ascii.length / 2];
				ascii2Hex(iv_asc.length, iv_asc, iv);
				ascii2Hex(prikey_asc.length, prikey_asc, unwrappedkey);
				ascii2Hex(enc_ascii.length, enc_ascii, decrypted);
				Cipher pri = Cipher.getInstance("RSA/NONE/PKCS1Padding", "BC");
				pri.init(4, prikey);
				java.security.Key unwrappedKey = pri.unwrap(unwrappedkey,
						"DESEDE", 3);
				IvParameterSpec ivspec = new IvParameterSpec(iv);
				pri = Cipher.getInstance("DESEDE/OFB/NoPadding");
				pri.init(2, unwrappedKey, ivspec);
				byte decryptout[] = pri.doFinal(decrypted);
				return new String(decryptout,this.chartset);
			} else {
				Cipher pri = Cipher.getInstance("RSA/NONE/PKCS1Padding", "BC");
				pri.init(2, prikey);
				byte enc_ascii[] = tobeDecrypted.getBytes(this.chartset);
				byte decrypted[] = new byte[enc_ascii.length / 2];
				ascii2Hex(enc_ascii.length, enc_ascii, decrypted);
				byte decryptout[] = pri.doFinal(decrypted);
				return new String(decryptout,this.chartset);
			}
			
		} catch (IOException e) {
			throw new Exception("Error Number:-10005, Error Description: ER_FIND_CERT_FAILED"
					+ e.getMessage());
			
		} catch (GeneralSecurityException e) {
			throw new Exception( "Error Number:-10023, Error Description: ER_DECRYPT_ERROR"
					+ e.getMessage());
			
		} finally {
			try {
				if (in != null){
					in.close();
				}
			} catch(IOException e){
				//
			}
		}
	}

	/**
	 * pkcs证书签名
	 * @param tobeSigned
	 * @param keyFile
	 * @param password
	 * @return
	 */
	public String signMsg (String tobeSigned, String keyFile, String password)throws Exception{
		return this.signMsg(tobeSigned, keyFile, password,"");
	}
	
	/**
	 * jfc证书签名
	 * @param tobeSigned
	 * @param keyFile
	 * @param password
	 * @param alias
	 * @return
	 */
	public String signMsg(String tobeSigned, String keyFile, String password, String alias) throws Exception{
		InputStream in = null;
		try {
			KeyStore ks ;
			PrivateKey prikey ;
			in = this.getClass().getClassLoader().getResourceAsStream(keyFile);
			
			if (this.cAType.equals(PKCS12)){
				ks = KeyStore.getInstance("PKCS12");
				ks.load(in, password.toCharArray());
				Enumeration<String> myEnum = ks.aliases();
				String keyAlias = null;
				keyAlias = (String) myEnum.nextElement();
				prikey = (PrivateKey) ks.getKey(keyAlias, password
						.toCharArray());
			} else {
				ks = KeyStore.getInstance("JKS");

				if (this.keyStorePass==null
						|| this.keyStorePass.equals("")) {
					throw new Exception( "Error Number:-10008, Error Description: keystore password can't null");
					
				}

				ks.load(in, this.keyStorePass
						.toCharArray());
				prikey = (PrivateKey) ks.getKey(alias, password
						.toCharArray());
			}			
			
			Signature sign = Signature.getInstance("SHA1withRSA");
			sign.initSign(prikey);
			sign.update(tobeSigned.getBytes(this.chartset));
			byte signed[] = sign.sign();
			byte sign_asc[] = new byte[signed.length * 2];
			hex2Ascii(signed.length, signed, sign_asc);
			return new String(sign_asc,this.chartset);
			
		} catch (IOException e) {
			throw new Exception( "Error Number:-10005, Error Description: ER_FIND_CERT_FAILED"
					+ e.getMessage());
			//return false;
		} catch (GeneralSecurityException e) {
			e.printStackTrace();
			throw new Exception( "Error Number:-10020, Error Description: ER_SIGN_ERROR"
					+ e.getMessage());
			
			//return false;
		} finally {
			try {
				if (in != null){
					in.close();
				}
			} catch(IOException e){
				//
			}
		}
	}
	
	/**
	 * pkcs12证书 验证签名信息
	 * @param tobeVerified
	 * @param plainText
	 * @param certFile
	 * @return
	 */
	public boolean verifyMsg(String tobeVerified, String plainText,
			String certFile) throws Exception{
		return this.verifyMsg(tobeVerified, plainText, certFile,"");
	}

	/**
	 * jks证书验证签名信息
	 * @param tobeVerified
	 * @param plainText
	 * @param certFile
	 * @param alias
	 * @return
	 */
	public boolean verifyMsg(String tobeVerified, String plainText,
			String certFile, String alias) throws Exception{
		InputStream certfile = null;
		try {
			
			certfile = this.getClass().getClassLoader().getResourceAsStream(certFile);
			RSAPublicKey pubkey = null;
			if (this.cAType.equals(PKCS12)) {
				CertificateFactory cf = CertificateFactory.getInstance("X.509");
				X509Certificate x509cert = (X509Certificate) cf
						.generateCertificate(certfile);
				pubkey = (RSAPublicKey) x509cert.getPublicKey();
			} else if (this.cAType.equalsIgnoreCase(JKS)){
				KeyStore ks=KeyStore.getInstance("JKS");
				if (this.keyStorePass==null|| this.keyStorePass.equals("")){
					throw new Exception( "Error Number:-10008, Error Description: keystore password can't null");
					
				}
				ks.load(certfile,this.keyStorePass.toCharArray());
				if (alias==null||alias.equals("")){
					throw new Exception("Error Number:-10007, Error Description: alias can't null");
					
				}
				java.security.cert.Certificate c=ks.getCertificate(alias);//alias为条目的别名
				pubkey = (RSAPublicKey)c.getPublicKey();
				
			}
			
			Signature verify = Signature.getInstance("SHA1withRSA");
			verify.initVerify(pubkey);
			byte signeddata[] = new byte[tobeVerified.length() / 2];
			ascii2Hex(tobeVerified.length(), tobeVerified.getBytes(this.chartset),
					signeddata);
			verify.update(plainText.getBytes(this.chartset));
			if (verify.verify(signeddata))
				return true;
			else {
				//LastErrMsg = "Error Number:-10021, Error Description:ER_VERIFY_ERROR";
				return false;
			}
		} catch (GeneralSecurityException e) {
			throw new Exception("Error Number:-10021, Error Description: ER_VERIFY_ERROR"
					+ e.getMessage());
			//return false;
		} catch (IOException e) {
			throw new Exception("Error Number:-10016, Error Description: ER_CERT_PARSE_ERROR"
					+ e.getMessage());
			

		} finally {
			try {
				if (certfile != null)
					certfile.close();
			} catch (IOException e) {

			}
		}
	}

	
	private static void hex2Ascii(int len, byte dataIn[], byte dataOut[]) {
		byte temp1[] = new byte[1];
		byte temp2[] = new byte[1];
		int i = 0;
		int j = 0;
		for (; i < len; i++) {
			temp1[0] = dataIn[i];
			temp1[0] = (byte) (temp1[0] >>> 4);
			temp1[0] = (byte) (temp1[0] & 0xf);
			temp2[0] = dataIn[i];
			temp2[0] = (byte) (temp2[0] & 0xf);
			if (temp1[0] >= 0 && temp1[0] <= 9)
				dataOut[j] = (byte) (temp1[0] + 48);
			else if (temp1[0] >= 10 && temp1[0] <= 15)
				dataOut[j] = (byte) (temp1[0] + 87);
			if (temp2[0] >= 0 && temp2[0] <= 9)
				dataOut[j + 1] = (byte) (temp2[0] + 48);
			else if (temp2[0] >= 10 && temp2[0] <= 15)
				dataOut[j + 1] = (byte) (temp2[0] + 87);
			j += 2;
		}

	}

	private static void ascii2Hex(int len, byte dataIn[], byte dataOut[]) {
		byte temp1[] = new byte[1];
		byte temp2[] = new byte[1];
		int i = 0;
		for (int j = 0; i < len; j++) {
			temp1[0] = dataIn[i];
			temp2[0] = dataIn[i + 1];
			if (temp1[0] >= 48 && temp1[0] <= 57) {
				temp1[0] -= 48;
				temp1[0] = (byte) (temp1[0] << 4);
				temp1[0] = (byte) (temp1[0] & 0xf0);
			} else if (temp1[0] >= 97 && temp1[0] <= 102) {
				temp1[0] -= 87;
				temp1[0] = (byte) (temp1[0] << 4);
				temp1[0] = (byte) (temp1[0] & 0xf0);
			}
			if (temp2[0] >= 48 && temp2[0] <= 57) {
				temp2[0] -= 48;
				temp2[0] = (byte) (temp2[0] & 0xf);
			} else if (temp2[0] >= 97 && temp2[0] <= 102) {
				temp2[0] -= 87;
				temp2[0] = (byte) (temp2[0] & 0xf);
			}
			dataOut[j] = (byte) (temp1[0] | temp2[0]);
			i += 2;
		}

	}

	public String getcAType() {
		return cAType;
	}

	public void setcAType(String type) {
		cAType = type;
	}

	public String getKeyStorePass() {
		return keyStorePass;
	}

	public void setKeyStorePass(String keyStorePass) {
		this.keyStorePass = keyStorePass;
	}
	
	public static void main(String[] args) throws Exception {
		NetTrans netTrans = new NetTrans("PKCS12", "GBK");
		netTrans.encryptMsg("aaaaaaaaaa", "security/ups.cer");
	}

}